Skip to main content

Advanced Express

Kamal [KL]
Kamal [KL]Netlab Assistant 2024/2025

Untuk meningkatkan backend Express.js, ada beberapa aspek penting yang perlu diperhatikan, seperti keamanan (security), skalabilitas (scalability), dan penanganan kesalahan (error handling). Berikut adalah penjelasan mengenai masing-masing aspek beserta contoh kode yang relevan.

1. Keamanan (Security)

Keamanan adalah aspek krusial dalam pengembangan backend. Beberapa teknik yang dapat diterapkan antara lain:

a. Hashing

Hashing digunakan untuk mengamankan data sensitif seperti kata sandi. Algoritma seperti SHA dan MD5 sudah tidak direkomendasikan karena rentan terhadap serangan. Sebagai gantinya, gunakan bcrypt yang lebih aman.

Berikut adalah perbedaan utama antara SHA, MD5, dan bcrypt:

  • SHA (Secure Hash Algorithm): Dirancang untuk menghasilkan hash unik dari data input. Contohnya adalah SHA-256 dan SHA-512. Namun, algoritma ini tidak cocok untuk hashing kata sandi karena hashingnya terlalu cepat, sehingga rentan terhadap serangan brute force.
  • MD5 (Message Digest 5): Algoritma hashing yang lebih tua dan menghasilkan hash 128-bit. MD5 sudah dianggap tidak aman karena rentan terhadap serangan collision.
  • bcrypt: Dirancang khusus untuk hashing kata sandi. bcrypt menggunakan salt untuk memastikan hash yang dihasilkan unik, mendukung work factor untuk meningkatkan keamanan, dan lebih lambat dibandingkan SHA dan MD5, sehingga lebih tahan terhadap brute force.

Contoh penggunaan bcrypt:

const bcrypt = require("bcrypt");
const saltRounds = 10;
const plainPassword = "yourPassword";

// Hashing password
bcrypt.hash(plainPassword, saltRounds, function (err, hash) {
// Store hash in your password DB.
});

// Verifikasi password
bcrypt.compare(plainPassword, hash, (err, result) => {
if (err) throw err;
if (result) {
// Password cocok
} else {
// Password tidak cocok
}
});

b. CORS (Cross-Origin Resource Sharing)

CORS mengatur bagaimana sumber daya di server dapat diakses oleh domain lain. Untuk mengelola CORS di Express.js, Anda dapat menggunakan middleware cors.

Contoh penerapan CORS:

const express = require("express");
const cors = require("cors");
const app = express();

const corsOptions = {
origin: "https://example.com",
methods: ["GET", "POST", "PUT", "DELETE"],
};

app.use(cors(corsOptions));

// Rute lainnya

c. Regex (Regular Expressions)

Regex (Regular Expressions) adalah pola teks yang digunakan untuk mencocokkan, mencari, atau memanipulasi string. Regex sangat berguna untuk validasi input, pencarian teks, atau penggantian teks dalam berbagai aplikasi.

Mari kita bedah regex yang digunakan dalam contoh:

const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

Contoh Input dan Output:

  • Valid:
    • example@example.com
    • user.name@domain.co
  • Tidak Valid:
    • example@ (tidak ada domain)
    • @example.com (tidak ada nama pengguna)
    • example@domain..com (titik ganda tidak diizinkan)
    • example@ domain.com (whitespace tidak diizinkan)

Cara Kerja Fungsi:

function validateEmail(email) {
return emailRegex.test(email);
}
  • emailRegex.test(email): Mengembalikan true jika string email cocok dengan pola regex, atau false jika tidak.

Berikut adalah beberapa karakter kunci dalam regex:

PatternPenjelasan
[ ]Untuk mendefinisikan range pencarian
[^ ]Untuk mencari karakter apa pun kecuali karakter di dalam tanda kurung siku
[A-Z]Untuk mencari huruf kapital (A sampai Z)
[a-z]Untuk mencari huruf non-kapital (a sampai z)
|Untuk mencari salah satu dari pola terkait (seperti OR)
.Untuk mencari hanya satu karakter apapun
^Untuk mencari awalan String
$Untuk mencari akhiran String
\dUntuk mencari digit angka
\sUntuk mencari whitespace
\bUntuk mencari awalan dan/atau akhiran kalimat
\WUntuk mencari karakter non-alphanumeric (special characters)
\wUntuk mencari karakter alphanumeric (huruf, angka, dan underscore)
\uxxxxUntuk mencari karakter unicode dalam hex
n+Untuk mencari String apa saja minimal 1
n*Untuk mencari String yang terdapat n (minimal 0 atau lebih)
n?Untuk mencari apakah ada n dalam String tersebut
n{x}Untuk mencari karakter sebanyak x
n{x,y}Untuk mencari karakter sebanyak range x sampai y

Kelebihan Regex:

  • Sangat fleksibel untuk mencocokkan pola teks yang kompleks.
  • Dapat digunakan di berbagai bahasa pemrograman.

Kekurangan Regex:

  • Sulit dibaca dan dipahami, terutama untuk pola yang kompleks.
  • Tidak selalu cocok untuk semua jenis validasi (misalnya, validasi email yang lebih ketat memerlukan library khusus).

2. Skalabilitas (Scalability)

Untuk memastikan aplikasi dapat menangani peningkatan beban kerja, pertimbangkan hal-hal berikut:

a. Membuat dan melepas koneksi PostgreSQL untuk setiap query

Mengelola koneksi database dengan efisien sangat penting. Gunakan pool koneksi untuk mengurangi overhead pembuatan koneksi baru setiap kali melakukan query.

Contoh penggunaan pool koneksi dengan pg:

const { Pool } = require("pg");
const pool = new Pool({
connectionString: PG_CONNECTION_STRING,
ssl: {
rejectUnauthorized: false,
},
});

// Membuat function query yang membuat lalu melepas koneksi
const query = async (text, params) => {
let client;
try {
client = await pool.connect();
const result = await client.query(text, params);
return result;
} catch (err) {
console.error("Database Query Error:", err);
throw err;
} finally {
if (client) client.release();
}
};

module.exports = {
pool,
query,
};

b. Efisiensi Query SQL

Contoh terdapat terdapat array yang berisi beberapa ID. terdapat beberapa metode untuk mengambil data pengguna berdasarkan ID tersebut:

  • Menggunakan .map dan Promise
const ids = [
"550e8400-e29b-41d4-a716-446655440000",
"550e8400-e29b-41d4-a716-446655440001",
"550e8400-e29b-41d4-a716-446655440002",
];
const promises = ids.map((id) => {
return pool.query("SELECT * FROM users WHERE id = $1", [id]);
});

Promise.all(promises)
.then((results) => {
// Menggabungkan hasil
const users = results.map((r) => r.rows[0]);
})
.catch((err) => {
// Tangani kesalahan
});

Dengan menggunakan Promise.all, Anda dapat menjalankan beberapa query secara paralel dan menggabungkan hasilnya.

  • Menggunakan query dengan klausa WHERE IN
const ids = [
"550e8400-e29b-41d4-a716-446655440000",
"550e8400-e29b-41d4-a716-446655440001",
"550e8400-e29b-41d4-a716-446655440002",
];
const query = "SELECT * FROM users WHERE id = ANY($1::uuid[])";

pool.query(query, [ids], (err, res) => {
if (err) throw err;
const users = res.rows;
// Proses data pengguna
});

Metode kedua lebih efisien karena hanya melakukan satu query ke database. Namun, metode pertama lebih fleksibel jika Anda perlu melakukan operasi tambahan pada setiap hasil query.

3. Penanganan Kesalahan (Error Handling)

Penanganan kesalahan yang baik meningkatkan keandalan aplikasi.

a. Memastikan body atau header yang diperlukan diberikan

Contoh pengecekan parameter body :

exports.login = async (req, res) => {
// Memeriksa keberadaan body
if (!req.query.email || !req.query.password) {
return res.status(400).send("Missing email or password");
}
try {
// Proses login
} catch (error) {
// Tangani kesalahan
}
};

b. Menggunakan BEGIN, COMMIT, atau ROLLBACK untuk query yang mengubah data

Dalam operasi yang melibatkan beberapa query yang saling bergantung, gunakan transaksi untuk memastikan konsistensi data.

Contoh penggunaan transaksi dengan pg:

require("dotenv").config();
const { Pool } = require("pg");

const { PG_CONNECTION_STRING } = process.env;

const pool = new Pool({
connectionString: PG_CONNECTION_STRING,
ssl: {
rejectUnauthorized: false,
},
});

const transaction = async (callback) => {
let client;
try {
client = await pool.connect();
await client.query("BEGIN");
const result = await callback(client);
await client.query("COMMIT");
return result;
} catch (err) {
await client.query("ROLLBACK");
console.error("Transaction Error:", err);
throw err;
} finally {
if (client) client.release();
}
};

module.exports = {
pool,
transaction,
};

Dengan menerapkan praktik-praktik di atas, Anda dapat meningkatkan keamanan, skalabilitas, dan keandalan backend Express.js Anda.